/************************************************************************
 File:			Ethernet.cpp
 Author:		Justin England
 Class:			CSC 470
 Instructor:	Dr. Penaloza
 Date:
 Description:	Class file for ethernet representation.
 
 Known bugs/missing features:
 Modifications:
 Date                Comment            
 ----    ------------------------------------------------
 ************************************************************************/

#include <QtGui>
#include <QtDebug>
#include "Ethernet.h"
#include "Master.h"

/************************************************************************
 Function:		Ethernet()
 Author:		Justin England
 
 Description:	This is the constructor for the ethernet class.  It
				is passed the parameters needed to run the simulation
				with the options selected within the GUI.
 
				Creates X number of master ojects (X selected from GUI)
				and that same number of threads for each master oject.

 Parameters:	<**** ADD PARAMS THAT ARE USED IN SIM ****>
 
 ************************************************************************/

Ethernet::Ethernet(QWidget *parent): QWidget(parent)
{


}


/************************************************************************
 Function:		~Ethernet()
 Author:		Justin England
 
 Description:	Destructor for ethernet oject.  Loops through each
				master thread and terminates them
 Parameters:
 ************************************************************************/

Ethernet::~Ethernet()
{
        stop();
}

/************************************************************************
 Function:		start()
 Author:		Justin England
 
 Description:	This method starts the ethernet simulation.  It loops
				through each master object and calls their start()
				method.  Therefore, each master will start generating
				packets to send to the ethernet.
 
 Parameters:	<none>
 
 ************************************************************************/

void Ethernet::start(int masters, int maxpacket, int minpacket, int speed)
{

    theNet.masters		=  masters;
    theNet.maxPacketSize        = maxpacket;
    theNet.minPacketSize        = minpacket;
    theNet.speed                = speed;

    theNet.currentState = Idle;

    qDebug() << "Connecting masters";

    for(int i = 0; i < theNet.masters; i++)
    {
            // create a new master object and append to netMasters list,
            // passing it's master # (the i value) and the total number of masters
            netMasters.append(new Master(i, theNet.masters));

            // attach the master to this ethernet object by passing
            // the 'this' pointer
            netMasters.at(i)->attachToEthernet(this);

            // create a new thread and append to masterThreads list
            masterThreads.append(new QThread());

            // move the master ojbect to the newly create thread
            netMasters.at(i)->moveToThread(masterThreads.at(i));

            // tell the thread to start executing
            masterThreads.at(i)->start();
    }

	for(int i = 0; i < theNet.masters; i++)
		netMasters.at(i)->start();

        qDebug() << "Started Ok";
}

/************************************************************************
 Function:		start()
 Author:		Justin England
 
 Description:	This method stops all masters from sending packets to
				the ethernet
 
 Parameters:	<none>
 
 ************************************************************************/

void Ethernet::stop(void)
{

    qDebug() << "Stopping all masters";
        for (int i = 0; i < theNet.masters; i++)
        {
                    // stop master timers
                    netMasters.at(i)->stop();

                    // end all threads
                    masterThreads.at(i)->quit();

                    // delete master object
                    delete netMasters.at(i);

                    // wait until thread has stopped
                    while(masterThreads.at(i)->isRunning()) {}

                    // delete thread
                    delete masterThreads.at(i);

        }

            // just to be safe, clear the masters and threads lists
            netMasters.clear();
            masterThreads.clear();

            // clear the ethernet
        dataPacket.clear();

            // make sure the mutex is unlocked
        packetMutex.unlock();

        // reset theNet structure
            // not sure we should do this here before collecting the stats

            theNet.masters			= 0;
            theNet.packets			= 0;
            theNet.collisions		= 0;
            theNet.minPacketSize	= 0;
            theNet.maxPacketSize	= 0;
            theNet.speed			= 0;
            theNet.currentState		= Idle;

            qDebug() << "All threads have stopped";


}

/************************************************************************
 Function:		isBusy()
 Author:		Justin England
 
 Description:	returns TRUE if the ethernet is currently busy,
				FALSE if it is available
 
 Parameters:	<none>
 Returns:		boolean value - see description
 
 ************************************************************************/

bool Ethernet::isBusy(int master)
{
	// returns the state of the ethernet to calling master
	// used to determine if the ethernet is available to
	// receive a packet from sending master
	
        //emit masterActionSignal( master , 3 ); //checking

	if (theNet.currentState == Idle)
	{
                //emit enetStatusSignal( 0 );
		return FALSE;
	}
        //emit enetStatusSignal( 2 );
	return TRUE;
}

/************************************************************************
 Function:		putOnEthernet()
 Author:		Justin England
 
 Description:	This receives a "packet" from a master object and 
				puts it into the ethernet queue
 
 Parameters:	int packet	- an int value that represents a packet
							- generated randomly by master object
 
 ************************************************************************/

void Ethernet::putOnEthernet(int packet)
{
	// this method does nothing more than add a "packet" to a queue
	// -- there should only be one packet in the queue at any given
	// -- time, if there are more than one packet (as determined in 
	// -- the acceptPacket method,) a collision occured
	
	packetMutex.lock();				// lock mutex to aviod concurrent access
	dataPacket.append(packet);		// add packet to queue
	packetMutex.unlock();			// unlock mutex
}

/************************************************************************
 Function:		acceptPacket()
 Author:		Justin England
 
 Description:	Requested from master object to determine if the packet
				was successfully sent to the ethernet or if a collision
				occured.
 
 Parameters:	int sender		- the ID of the sending master
				int receiver	- the ID of the receiving master
 
 Returns:		bool	TRUE	- the packet was sent successfully
						FALSE	- a collision occured on the ethernet
 
 ************************************************************************/

bool Ethernet::acceptPacket(int sender, int receiver /* packetSize */)
{
    // this will lock all data that is modified within this method
    // and the MutexLocker will release the lock once it goes out
    // of scope (at the return locations);  the mutexlocker
    // makes it easier to lock / unlock the mutex
//	QMutexLocker locker(&packetMutex);
    if(!packetMutex.tryLock())
    {
            qDebug() << "Collision";

            return FALSE;
    }

    // check if there are multiple packets to process
    // if so, there is a collision, record and return false
    // to calling master so that it's aware there was a
    // collision
    if(dataPacket.size() > 1)		// collision occured
    {
            emit enetStatusSignal( 1 );
            dataPacket.clear();			// clear ethernet
            theNet.collisions++;		// increment collision counter
            packetMutex.unlock();
            return FALSE;				// return FASLE to calling master

    }

    // since the only way the dataPacket queue can be empty is if
    // another thread cleared it (see above) therefore, also notify this
    // calling thread that a collision occured.  IE  For a collision
    // to occur, two masters are sending at the same time, the first
    // was notified above, now we notify the second master
    // Also note, the since this is the ethernet object, only one collision
    // occured, therefore, we only update the counter once (as done above)
    if(dataPacket.isEmpty())
    {
            packetMutex.unlock();
            return FALSE;				// return FALSE to calling master
    }

    //qDebug() << "Start - Sender: " << sender << " Receiver: " << receiver;
    // no collision occured, process packet
    emit enetStatusSignal( 2 );   //busy
    emit masterActionSignal( sender , 2 );  //sending signal
    emit masterActionSignal( receiver , 1 );  //receive signal

    theNet.currentState = Busy;		// set ethernet state to busy
    theNet.packets++;				// increment packet count

    // process packet speed / data size
    // this should have some algroithm to create a delay based
    // off the packet size and the speed;  The packet size could be
    // a function of the random INT that is found in the dataPacket queue


    netMasters.at(receiver)->rxPacket();	// notify receiver of packet


    //theNet.currentState = Idle;				// set ethernet back to idle
    dataPacket.clear();						// clear packet from ethernet
    currentSender = sender;
    currentReceiver = receiver;
    int packetSize=0;
    do
    {
        packetSize = random() % theNet.maxPacketSize;

    }while(packetSize < theNet.minPacketSize);

    QTimer::singleShot(25 * (packetSize/theNet.speed), this, SLOT(freeEthernet()));
    return TRUE;							// packet successful
}

void Ethernet::freeEthernet()
{
    emit enetStatusSignal( 0 );
      //qDebug() << "Sender: " << currentSender << "  Receiver: " << currentReceiver;
      emit masterActionSignal( currentSender , 0 );  //sending signal
      emit masterActionSignal( currentReceiver , 0 );  //sending signal
      theNet.currentState = Idle;

      packetMutex.unlock();


}


